/* sane - Scanner Access Now Easy.

   Copyright (C) 2004 - 2006 Gerard Klaver  <gerard at gkall dot hobby dot nl>

   The teco2 and gl646 backend (Frank Zago) are used as a template for 
   this backend.

   For the usb commands and bayer decoding parts of the following 
   program are used:
   The pencam2 program  (GNU GPL license 2)

   For the usb commands parts of the following programs are used:
   The libgphoto2 (camlib stv0680)   (GNU GPL license 2)
   The stv680.c/.h kernel module   (GNU GPL license 2)

   For the stv680_add_text routine the add_text routine and font_6x11.h file 
   are taken from the webcam.c file, part of xawtv program,
   (c) 1998-2002 Gerd Knorr (GNU GPL license 2).

   This file is part of the SANE package.
   
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.
   
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
   MA 02111-1307, USA.
   ------------------------------------------------------------------
*/

/*  $Id: stv680.c,v 1.1 2006-06-05 17:59:26 gekl-guest Exp $

   stv680 vidcam  driver Gerard Klaver
*/

/*SANE FLOW DIAGRAM

   - sane_init() : initialize backend, attach vidcams
   . - sane_get_devices() : query list of vidcam devices
   . - sane_open() : open a particular vidcam device
   . . - sane_set_io_mode : set blocking mode
   . . - sane_get_select_fd : get vidcam fd
   . . - sane_get_option_descriptor() : get option information
   . . - sane_control_option() : change option values
   . .
   . . - sane_start() : start image acquisition
   . .   - sane_get_parameters() : returns actual scan parameters
   . .   - sane_read() : read image data (from pipe)
   . .     (sane_read called multiple times; 
   . .      after sane_read returns EOF)
   . .     go back to sane_start() if more frames desired
   . . - sane_cancel() : cancel operation
   . - sane_close() : close opened vidcam device
   - sane_exit() : terminate use of backend
*/
/*--------------------------------------------------------------------------*/

#define BUILD 1			/* 2004/09/09  update 20-04-2006 */
#define BACKEND_NAME stv680
#define STV680_CONFIG_FILE "stv680.conf"

/* --------------------- SANE INTERNATIONALISATION ------------------------ */

/* must be first include */
#include "sane/config.h"

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "sane/sane.h"
#include "sane/sanei.h"
#include "sane/saneopts.h"
#include "sane/sanei_usb.h"
#include "sane/sanei_debug.h"
#include "sane/sanei_backend.h"
#include "sane/sanei_config.h"
#include "lassert.h"

/* for add-text routine  */
#include <time.h>
#include "font_6x11.h"
/*-----------------------*/

#include "stv680.h"

#define TIMEOUT 1000

/*--------------------------------------------------------------------------*/
/* Lists of possible scan modes. */
static SANE_String_Const scan_mode_list[] = {
	COLOR_RGB_STR,
	COLOR_RGB_TEXT_STR,
	SANE_VALUE_SCAN_MODE_COLOR,
	COLOR_RAW_STR,

	NULL
};

/*-----------------------------------------minium, maximum, quantization----*/
static const SANE_Range brightness_range = { -128, 128, 1 };

static const SANE_Range red_level_range = { -32, 32, 1 };

static const SANE_Range green_level_range = { -32, 32, 1 };

static const SANE_Range blue_level_range = { -32, 32, 1 };

/*--------------------------------------------------------------------------*/

static const struct dpi_color_adjust stv680_dpi_color_adjust[] = {

	/*dpi, y, x, color sequence R G or B */
	{176, 144, 0, 1, 2},	/* QCIF  selected by dev->CIF  */
	{352, 288, 0, 1, 2},	/* CIF            ,,           */
	{160, 120, 0, 1, 2},	/* QSIF           ,, dev->VGA  */
	{320, 240, 0, 1, 2},	/* QVGA (SIF)     ,,           */
	{640, 480, 0, 1, 2},	/* VGA            ,,           */
	/* must be the last entry */
	{0, 0, 0, 0, 0}
};

static const struct vidcam_hardware vidcams[] = {

	{0x0553, 0x0202, USB_CLASS_VENDOR_SPEC,
	 "AIPTEK", "PENCAM STV0680",
	 stv680_dpi_color_adjust},

	{0x04c8, 0x0722, USB_CLASS_VENDOR_SPEC,
	 "Konica", "e-mini",
	 stv680_dpi_color_adjust},

	{0x1183, 0x0001, USB_CLASS_VENDOR_SPEC,
	 "DigitalDream", "l'espion XS",
	 stv680_dpi_color_adjust},

	{0x041e, 0x4007, USB_CLASS_VENDOR_SPEC,
	 "Creative", "WebCam Go mini",
	 stv680_dpi_color_adjust}
};

/* List of vidcams attached. */
static Stv680_Vidcam *first_dev = NULL;
static int num_devices = 0;
/* used by sane_get_devices */
static const SANE_Device **devlist = NULL;

/*----------------------------------------------------------- */

/* Local functions. */

/* Display a buffer in the log. Display by lines of 16 bytes. */
static void
hexdump(int level, const char *comment, unsigned char *buf, const int length)
{
	int i;
	char line[128];
	char *ptr;
	char asc_buf[17];
	char *asc_ptr;

	DBG(level, "  %s\n", comment);

	i = 0;
	goto start;

	do {
		if (i < length) {
			ptr += sprintf(ptr, " %2.2x", *buf);

			if (*buf >= 32 && *buf <= 127) {
				asc_ptr += sprintf(asc_ptr, "%c", *buf);
			} else {
				asc_ptr += sprintf(asc_ptr, ".");
			}
		} else {
			/* After the length; do nothing. */
			ptr += sprintf(ptr, "   ");
		}

		i++;
		buf++;

		if ((i % 16) == 0) {
			/* It's a new line */
			DBG(level, "  %s    %s\n", line, asc_buf);

		      start:
			ptr = line;
			*ptr = '\0';
			asc_ptr = asc_buf;
			*asc_ptr = '\0';

			ptr += sprintf(ptr, "  %3.3d:", i);
		}
	}
	while (i < ((length + 15) & ~15));
}

/* Returns the length of the longest string, including the terminating
 * character. */
static size_t
max_string_size(SANE_String_Const strings[])
{
	size_t size, max_size = 0;
	int i;

	for (i = 0; strings[i]; ++i) {
		size = strlen(strings[i]) + 1;
		if (size > max_size) {
			max_size = size;
		}
	}
	return max_size;
}

/* Initialize a vidcam entry. Return an allocated vidcam with some
 *  */
static Stv680_Vidcam *
stv680_init(void)
{
	Stv680_Vidcam *dev;

	DBG(DBG_proc, "stv680_init: enter\n");

	/* Allocate a new vidcam entry. */
	dev = malloc(sizeof(Stv680_Vidcam));
	if (dev == NULL) {
		return NULL;
	}
	memset(dev, 0, sizeof(Stv680_Vidcam));

/* Allocate the windoww buffer*/
	dev->windoww_size = 0x20;
	dev->windoww = malloc(dev->windoww_size);
	if (dev->windoww == NULL) {
		free(dev);
		return NULL;
	}

/* Allocate the windowr buffer*/
	dev->windowr_size = 0x20;
	dev->windowr = malloc(dev->windowr_size);
	if (dev->windowr == NULL) {
		free(dev);
		free(dev->windoww);
		return NULL;
	}

	dev->fd = -1;

	DBG(DBG_proc, "stv680_init: exit\n");

	return (dev);
}

static SANE_Status
stv680_init_2(Stv680_Vidcam * dev)
{
	SANE_Status status;

	DBG(DBG_proc, "stv680_init_2: enter\n");

	/* Allocate the buffer used to transfer the USB data */
	/* Check for max. format image size so buffer size can
	 * be adjusted, format from camera is bayer 422 */
	if (dev->CIF)
		dev->buffer_size = 356 * 292;
	if (dev->VGA)
		dev->buffer_size = 644 * 484;
	DBG(DBG_proc, "stv680_init_2: dev->bufffer = 0x%lx\n",
	    (unsigned long) (size_t) dev->buffer_size);

	dev->buffer = malloc(dev->buffer_size);

	if (dev->buffer == NULL) {
		free(dev);
		free(dev->windowr);
		free(dev->windoww);
		return SANE_STATUS_NO_MEM;
	}

	/* Allocate the output buffer used for bayer conversion */
	dev->output_size = dev->buffer_size * 3;

	dev->output = malloc(dev->output_size);
	if (dev->output == NULL) {
		free(dev);
		free(dev->windowr);
		free(dev->windoww);
		free(dev->buffer);

		return SANE_STATUS_NO_MEM;
	}
	dev->image_size = dev->buffer_size;

	dev->image = malloc(dev->image_size);
	if (dev->image == NULL) {
		free(dev);
		free(dev->windowr);
		free(dev->windoww);
		free(dev->buffer);
		free(dev->output);

		return SANE_STATUS_NO_MEM;
	}

	DBG(DBG_proc, "stv680_init_2: exit\n");
	status = SANE_STATUS_GOOD;
	return status;
}

/* Closes an open vidcams. */
static void
stv680_close(Stv680_Vidcam * dev)
{
	DBG(DBG_proc, "stv680_close: enter \n");

	if (dev->fd != -1) {

		DBG(DBG_proc, "stv680_close: fd !=-1 \n");
		sanei_usb_close(dev->fd);
		dev->fd = -1;
	}

	DBG(DBG_proc, "stv680_close: exit\n");
}

/* Frees the memory used by a vidcam. */
static void
stv680_free(Stv680_Vidcam * dev)
{
	int i;

	DBG(DBG_proc, "stv680_free: enter\n");

	if (dev == NULL)
		return;

	stv680_close(dev);
	if (dev->devicename) {
		free(dev->devicename);
	}
	if (dev->buffer) {
		free(dev->buffer);
	}
	if (dev->output) {
		free(dev->output);
	}
	if (dev->image) {
		free(dev->image);
	}
	if (dev->windoww) {
		free(dev->windoww);
	}
	if (dev->windowr) {
		free(dev->windowr);
	}
	for (i = 1; i < OPT_NUM_OPTIONS; i++) {
		if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s) {
			free(dev->val[i].s);
		}
	}
	if (dev->resolutions_list)
		free(dev->resolutions_list);

	free(dev);

	DBG(DBG_proc, "stv680_free: exit\n");
}

static SANE_Status
stv680_set_config(Stv680_Vidcam * dev, int configuration, int interface,
		  int alternate)
{
	SANE_Status status;
	DBG(DBG_proc, "stv680_set_config: open\n");
/* seems a problem on some systems (Debian amd64 unstable 19042006)
 * not calling usb_set_configuration seems to help reason ?
  status = sanei_usb_set_configuration (dev->fd, configuration);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (DBG_error,
	   "stv680_set_config: STV680 FAILED to set configuration %d\n",
	   configuration);
      return status;
    }
*/
	status = sanei_usb_claim_interface(dev->fd, interface);
	if (status != SANE_STATUS_GOOD) {
		DBG(DBG_error,
		    "stv680_set_config: STV0680 FAILED to claim interface\n");
		return status;
	}

	status = sanei_usb_set_altinterface(dev->fd, alternate);
	if (status != SANE_STATUS_GOOD) {
		DBG(DBG_error,
		    "stv680_set_config: STV0680 FAILED to set alternate interface %d\n",
		    alternate);
		return status;
	}
	DBG(DBG_proc,
	    "stv680_set_config: configuration=%d, interface=%d, alternate=%d\n",
	    configuration, interface, alternate);

	DBG(DBG_proc, "stv680_set_config: exit\n");
	return status;
}

/* Reset vidcam */
static SANE_Status
stv680_reset_vidcam(Stv680_Vidcam * dev)
{
	SANE_Status status;
	size_t sizew;		/* significant size of window */
	size_t sizer;

	DBG(DBG_proc, "stv680_reset_vidcam: enter\n");

	sizew = dev->windoww_size;
	sizer = dev->windowr_size;

	memset(dev->windoww, 0, sizew);
	memset(dev->windowr, 0, sizer);

	sizew = 0x00;		/* was 0 ? */
	status = sanei_usb_control_msg(dev->fd, 0x41, 0x0a, 0x0000, 0, sizew,
				       dev->windoww);
	if (status != SANE_STATUS_GOOD) {
		return status;
	}
	DBG(DBG_proc, "stv680_reset_vidcam: CMDID_STOP_VIDEO end\n");

	/* this is a high priority command; it stops all lower order commands */

	sizew = 0x00;		/* was 0 */
	status = sanei_usb_control_msg(dev->fd, 0x41, 0x04, 0x0000, 0, sizew,
				       dev->windoww);
	if (status != SANE_STATUS_GOOD) {
		return status;
	}
	DBG(DBG_proc, "stv680_reset_vidcam: CMDID_CANCEL_TRANSACTION end\n");
	sizer = 0x02;
	DBG(DBG_proc, "stv680_reset_vidcam: CMDID_GET_LAST_ERROR begin\n");
	status = sanei_usb_control_msg(dev->fd, 0xc1, 0x80, 0x0000, 0, sizer,
				       dev->windowr);
	if (status != SANE_STATUS_GOOD) {
		/* Get Last Error; 2 = busy */
		DBG(DBG_proc,
		    "stv680_reset_vidcam: last error: %i, command = 0x%x\n",
		    dev->windowr[0], dev->windowr[1]);
		return status;
	} else {
		DBG(DBG_proc,
		    "stv680_reset_vidcam: Camera reset to idle mode.\n");
	}
	hexdump(DBG_info2, "stv680_reset_vidcam: CMDID_GET_LAST_ERROR",
		dev->windowr, sizer);

	/*  configuration = 1, interface = 0, alternate = 0 */
	/*
	   status = stv680_set_config (dev, 1, 0, 0);
	   if (status != SANE_STATUS_GOOD)
	   {
	   DBG (DBG_error,
	   "stv680_reset_vidcam: STV680 FAILED to set configure\n");
	   return status;
	   }
	 */
	status = SANE_STATUS_GOOD;
	DBG(DBG_proc, "stv680_reset_vidcam: exit\n");

	return status;
}

/* Inquiry a device and returns TRUE if is supported. */
static int
stv680_identify_vidcam(Stv680_Vidcam * dev)
{
	SANE_Status status;
	SANE_Word vendor;
	SANE_Word product;
	int i;
	size_t sizer;

	DBG(DBG_info, "stv680_identify_vidcam: open\n");

	status = sanei_usb_get_vendor_product(dev->fd, &vendor, &product);

	/* Loop through our list to make sure this scanner is supported. */
	for (i = 0; i < NELEMS(vidcams); i++) {
		if (vidcams[i].vendor == vendor
		    && vidcams[i].product == product) {

			DBG(DBG_info,
			    "stv680_identify_vidcam: vidcam %x:%x is in list\n",
			    vendor, product);

			dev->hw = &(vidcams[i]);

			sizer = dev->windowr_size;
			memset(dev->windowr, 0, sizer);

			/*  configuration = 1, interface = 0, alternate = 0 */
			/*
			   status = stv680_set_config (dev, 1, 0, 0);
			   if (status != SANE_STATUS_GOOD)
			   {
			   DBG (DBG_error,
			   "stv680_vidcam_init: STV680 FAILED to set configure\n");
			   return status;
			   }
			 */
			sizer = 0x02;
			status = sanei_usb_control_msg(dev->fd, 0xc1, 0x88,
						       0x5678, 0, sizer,
						       dev->windowr);
			if (status != SANE_STATUS_GOOD) {
				DBG(DBG_error,
				    "stv680_identify_vidcam: this is not a STV680 (idVendor = %d, bProduct = %d) writing register failed with %s\n",
				    vendor, product, sane_strstatus(status));
				return SANE_FALSE;
			}
			if ((dev->windowr[0] != 0x56)
			    || (dev->windowr[1] != 0x78)) {
				DBG(DBG_proc,
				    "STV(e): camera ping failed!!, checkvalue !=0x5678\n");
				sizer = 0x02;
				hexdump(DBG_info2, "urb12 window",
					dev->windowr, sizer);
				return SANE_FALSE;
			}
			sizer = 0x02;
			hexdump(DBG_info2, "urb12 ping data", dev->windowr,
				sizer);

			sizer = 0x10;
			status = sanei_usb_control_msg(dev->fd, 0xc1, 0x85,
						       0x0000, 0, sizer,
						       dev->windowr);
			if (status != SANE_STATUS_GOOD)
				return SANE_FALSE;

			hexdump(DBG_info2, "urbxx CMDID_GET_CAMERA_INFO",
				dev->windowr, sizer);

			dev->SupportedModes = dev->windowr[7];
			i = dev->SupportedModes;
			dev->QSIF = 0;
			dev->CIF = 0;
			dev->QCIF = 0;
			dev->VGA = 0;
			dev->QVGA = 0;
			if (i & 1)
				dev->CIF = 1;
			if (i & 2)
				dev->VGA = 1;
			if (i & 8)
				dev->QVGA = 1;
			if (i & 4)
				dev->QCIF = 1;
			dev->QSIF = dev->QVGA;	/* for software subsample */
			if (dev->SupportedModes == 0) {
				DBG(DBG_proc,
				    "STV(e): There are NO supported STV680 modes!!\n");
				i = -1;
				return SANE_FALSE;
			} else {
				if (dev->VGA)
					DBG(DBG_proc,
					    "STV(i): VGA is supported\n");
				if (dev->CIF)
					DBG(DBG_proc,
					    "STV(i): CIF is supported\n");
				if (dev->QVGA)
					DBG(DBG_proc,
					    "STV(i): QVGA is supported\n");
				if (dev->QCIF)
					DBG(DBG_proc,
					    "STV(i): QCIF is supported\n");
			}

			/* FW rev, ASIC rev, sensor ID  */
			DBG(DBG_proc, "STV(i): Firmware rev is %i.%i\n",
			    dev->windowr[0], dev->windowr[1]);
			DBG(DBG_proc, "STV(i): ASIC rev is %i.%i\n",
			    dev->windowr[2], dev->windowr[3]);
			DBG(DBG_proc, "STV(i): Sensor ID is %i.%i\n",
			    (dev->windowr[4]), (dev->windowr[5]));
			/* Hardware config  */
			dev->HardwareConfig = dev->windowr[6];
			i = dev->HardwareConfig;
			/* Comms link, Flicker freq, Mem size */
			if (i & 1)
				DBG(DBG_proc,
				    "STV(i): Comms link is serial\n");
			else
				DBG(DBG_proc, "STV(i): Comms link is USB\n");
			if (i & 2)
				DBG(DBG_proc,
				    "STV(i): Flicker freq = 60 Hz\n");
			else
				DBG(DBG_proc,
				    "STV(i): Flicker freq = 50 Hz\n");
			if (i & 4)
				DBG(DBG_proc, "STV(i): Mem size = 16Mbit\n");
			else
				DBG(DBG_proc, "STV(i): Mem size = 64Mbit\n");
			if (i & 8)
				DBG(DBG_proc,
				    "STV(i): Thumbnails supported\n");
			else
				DBG(DBG_proc, "STV(i): Thumbnails N/A\n");
			if (i & 16)
				DBG(DBG_proc, "STV(i): Video supported\n");
			else
				DBG(DBG_proc, "STV(i): Video N/A\n");
			if (i & 32)
				DBG(DBG_proc, "STV(i): Startup Complete\n");
			else
				DBG(DBG_proc,
				    "STV(i): Startup Not Complete\n");
			if (i & 64)
				DBG(DBG_proc, "STV(i): Monochrome\n");
			else
				DBG(DBG_proc, "STV(i): Color\n");
			if (i & 128)
				DBG(DBG_proc, "STV(i): Mem fitted\n");
			else
				DBG(DBG_proc, "STV(i): Mem not fitted\n");

			DBG(DBG_proc, "urb 25 CMDID_GET_IMAGE_INFO\n");
			sizer = 0x10;
			status = sanei_usb_control_msg(dev->fd, 0xc1, 0x86,
						       0x0000, 0, sizer,
						       dev->windowr);
			if (status != SANE_STATUS_GOOD) {
				return SANE_FALSE;
			}
			hexdump(DBG_info2, "urb25 CMDID_GET_IMAGE_INFO",
				dev->windowr, sizer);
			DBG(DBG_proc, "STV(i): Current image index %d\n",
			    ((dev->windowr[0] << 8) + (dev->windowr[1])));
			DBG(DBG_proc,
			    "If images are stored in camera, they will be lost when captering images is started!!!!!\n");
			DBG(DBG_proc, "STV(i): Max images %d\n",
			    ((dev->windowr[2] << 8) + (dev->windowr[3])));
			DBG(DBG_proc, "STV(i): Image width (pix) %d\n",
			    ((dev->windowr[4] << 8) + (dev->windowr[5])));
			DBG(DBG_proc, "STV(i): Image height (pix) %d\n",
			    ((dev->windowr[6] << 8) + (dev->windowr[7])));
			DBG(DBG_proc, "STV(i): Image size camera %d bytes\n",
			    ((dev->windowr[8] << 24) +
			     (dev->windowr[9] << 16) +
			     (dev->windowr[10] << 8) + (dev->windowr[11])));

			/*  configuration = 1, interface = 0, alternate = 1 */
			status = stv680_set_config(dev, 1, 0, 1);
			/*
			   if (status != SANE_STATUS_GOOD)
			   {
			   DBG (DBG_error,
			   "stv680_vidcam_init: STV680 FAILED to set configure\n");
			   return status;
			   }

			   DBG (DBG_info, "stv680_identify_vidcam: exit vidcam supported\n");
			 */
			return SANE_TRUE;
		}
	}
	DBG(DBG_error,
	    "stv680_identify_vidcam: exit this is not a STV680 exit\n");
	return SANE_FALSE;
}

static SANE_Status
stv680_vidcam_init(Stv680_Vidcam * dev)
{
	SANE_Status status;
	SANE_Byte i = 0;
	SANE_Byte val = 0;
	size_t sizer;
	size_t sizew;
	DBG(DBG_proc, "stv680_vidcam_init: open\n");

	sizew = dev->windoww_size;
	sizer = dev->windowr_size;

	memset(dev->windoww, 0, sizew);
	memset(dev->windowr, 0, sizer);

	DBG(DBG_proc, "stv680_vidcam_init: urb 13 CMDID_GET_USER_INFO\n");
	dev->video_status = 0x04;	/* dummy value busy */

	while (dev->video_status == 0x04) {
		sizer = 0x08;
		status = sanei_usb_control_msg(dev->fd, 0xc1, 0x8d, 0x0000, 0,
					       sizer, dev->windowr);
		if (status != SANE_STATUS_GOOD)
			return status;

		hexdump(DBG_info2,
			"stv680_vidcam_init: urb13 CMDID_GET_USER_INFO",
			dev->windowr, sizer);

		dev->video_status = dev->windowr[1];
		if (dev->video_status == 0x02) {
			DBG(DBG_proc, "stv680_vidcam_init: status = video\n");
		} else if ((dev->video_status == 0x01)
			   || (dev->video_status == 0x08)) {
			DBG(DBG_proc, "stv680_vidcam_init: status=%d\n",
			    dev->video_status);
		} else if (dev->video_status != 0x04) {
			DBG(DBG_proc, "stv680_vidcam_init: status = busy\n");
			/*  CMDID_CANCEL_TRANSACTION */
			status = sanei_usb_control_msg(dev->fd, 0x41, 0x04,
						       0x0000, 0, 0, 0);
			if (status != SANE_STATUS_GOOD) {
				DBG(DBG_info,
				    "stv680_vidcam_init: urb13 CMDID_CANCEL_TRANSACTION NOK\n");
				return status;
			}
		}
	}

	if (dev->video_status == 0x01 || dev->video_status == 0x08) {
		DBG(DBG_proc,
		    "stv680_vidcam_init: urb 21 CMDID_GET_COLDATA_SIZE\n");
		sizer = 0x02;
		status = sanei_usb_control_msg(dev->fd, 0xc1, 0x8a, 0x0000, 0,
					       sizer, dev->windowr);
		if (status != SANE_STATUS_GOOD)
			return status;

		val = dev->windowr[0];
		hexdump(DBG_info2,
			"stv680_vidcam_init: urb21 CMDID_GET_COLDATA_SIZE",
			dev->windowr, sizer);
		if (dev->windowr[0] &= 0x00)
			DBG(DBG_info,
			    "stv680_vidcam_init: no camera defaults, must be downloaded?\n");

		sizer = 0x10;
		for (i = 0; i < val; i += 0x10) {
			DBG(DBG_proc,
			    "stv680_vidcam_init: urb 22, 23, 24 CMDID_GET_COLDATA i=0x%x, val=0x%x\n",
			    i, val);
			status = sanei_usb_control_msg(dev->fd, 0xc1, 0x8b,
						       (i << 8), 0, sizer,
						       dev->windowr);
			if (status != SANE_STATUS_GOOD)
				return status;
			hexdump(DBG_info2,
				"stv680_vidcam_init: urb22, 23, 24 CMDID_GET_COLDATA",
				dev->windowr, sizer);
		}

		sizer = 0x12;
		status = sanei_usb_control_msg(dev->fd, 0x80, 0x06, 0x0100, 0,
					       sizer, dev->windowr);
		if (status != SANE_STATUS_GOOD)
			return status;
		/* if (!(i > 0) && (dev->windowr[8] == 0x53) && (dev->windowr[9] == 0x05))
		   {
		   DBG (DBG_proc, "STV(e): Could not get descriptor 0100.");
		 *//* return status;  *//*
		   } */
		sizer = 0x12;
		hexdump(DBG_info2,
			"stv680_vidcam_init: CMDID_SET_IMAGE_INDEX",
			dev->windowr, sizer);

		/*  configuration = 1, interface = 0, alternate = 1 */
		status = stv680_set_config(dev, 1, 0, 1);
		if (status != SANE_STATUS_GOOD) {
			DBG(DBG_error,
			    "stv680_vidcam_init: STV680 FAILED to set configure\n");
			return status;
		}
	}
	/*  Switch to Video mode: 0x0000 = CIF (352x288), 0x0200 = QCIF (176x144)  */
	/*  Switch to Video mode: 0x0100 = VGA (640x480), 0x0300 = QVGA (320x240)  */
	sizew = 0x0;
	status = sanei_usb_control_msg(dev->fd, 0x41, 0x09, dev->video_mode,
				       0, sizew, dev->windoww);
	if (status != SANE_STATUS_GOOD) {
		DBG(DBG_proc, "stv680_vidcam_init: video_mode = 0x%x\n",
		    dev->video_mode);
		return status;
	}
	DBG(DBG_proc,
	    "stv680_vidcam_init: CMDID_START_VIDEO: video_mode=0x%x\n",
	    dev->video_mode);

	if (dev->x_resolution == 176) {
		usleep(1000);	/* delay time needed */
	}
	status = SANE_STATUS_GOOD;

	if (status) {
		DBG(DBG_error, "stv680_vidcam_init failed : %s\n",
		    sane_strstatus(status));
		return status;
	}
	DBG(DBG_proc, "stv680_vidcam_init: exit\n");
	return status;
}

/* Attach a vidcam to this backend. */
static SANE_Status
attach_vidcam(SANE_String_Const devicename, Stv680_Vidcam ** devp)
{
	Stv680_Vidcam *dev;
	int fd;
	SANE_Status status;

	DBG(DBG_proc, "attach_vidcam: %s\n", devicename);

	if (devp)
		*devp = NULL;

	/* Check if we know this device name. */
	for (dev = first_dev; dev; dev = dev->next) {
		if (strcmp(dev->sane.name, devicename) == 0) {
			if (devp) {
				*devp = dev;
			}
			DBG(DBG_info, "device is already known\n");
			return SANE_STATUS_GOOD;
		}
	}

	/* Allocate a new vidcam entry. */
	dev = stv680_init();
	if (dev == NULL) {
		DBG(DBG_error, "stv680_init ERROR: not enough memory\n");
		return SANE_STATUS_NO_MEM;
	}

	DBG(DBG_info, "attach_vidcam: opening USB device %s\n", devicename);

	if (sanei_usb_open(devicename, &fd) != 0) {
		DBG(DBG_error, "ERROR: attach_vidcam: open failed\n");
		stv680_free(dev);
		return SANE_STATUS_INVAL;
	}
	/* Fill some scanner specific values. */
	dev->devicename = strdup(devicename);
	dev->fd = fd;

	/* Now, check that it is a vidcam we support. */

	if (stv680_identify_vidcam(dev) == SANE_FALSE) {
		DBG(DBG_error,
		    "ERROR: attach_vidcam: vidcam-identification failed\n");
		stv680_free(dev);
		return SANE_STATUS_INVAL;
	}

	/* Allocate a buffer memory. */
	status = stv680_init_2(dev);
	if (status != SANE_STATUS_GOOD) {
		DBG(DBG_error, "stv680_initi_2, ERROR: not enough memory\n");
		return SANE_STATUS_NO_MEM;
	}

	stv680_close(dev);

	DBG(DBG_info, "attach_vidcam: opening USB device %s\n", devicename);

	/* Build list of vidcam supported resolutions. */
	DBG(DBG_proc, "attach_vidcam: build resolution list\n");

	if (dev->hw->color_adjust[0].resolution_x != 0) {
		int num_entries;
		int i;
		num_entries = 0;

		while (dev->hw->color_adjust[num_entries].resolution_x != 0)
			num_entries++;

		dev->resolutions_list =
			malloc(sizeof(SANE_Word) * (num_entries + 1));

		if (dev->resolutions_list == NULL) {
			DBG(DBG_error,
			    "ERROR: attach_vidcam: vidcam resolution list failed\n");
			stv680_free(dev);
			return SANE_STATUS_NO_MEM;
		}
		/* for CIF or VGA sensor different resolutions  */
		if (dev->CIF)
			num_entries = 2;
		if (dev->VGA)
			num_entries = 3;
		dev->resolutions_list[0] = num_entries;
		DBG(DBG_proc,
		    "attach_vidcam: make color resolution table \n");
		for (i = 0; i < num_entries; i++) {
			dev->resolutions_list[i + 1 + dev->VGA + dev->QVGA] =
				dev->hw->color_adjust[i].resolution_x;
		}
	} else {
		dev->resolutions_list = NULL;
	}

	/* Set the default options for that vidcam. */
	dev->sane.name = dev->devicename;
	dev->sane.vendor = dev->hw->vendor_name;
	dev->sane.model = dev->hw->product_name;
	dev->sane.type = SANE_I18N("webcam");

	/* Link the vidcam with the others. */
	dev->next = first_dev;
	first_dev = dev;

	if (devp) {
		*devp = dev;
	}

	num_devices++;

	DBG(DBG_proc, "attach_vidcam: exit\n");

	return SANE_STATUS_GOOD;
}

static SANE_Status
attach_one(const char *dev)
{
	DBG(DBG_proc, "attach_one: open \n");
	attach_vidcam(dev, NULL);
	DBG(DBG_proc, "attach_one: exit \n");
	return SANE_STATUS_GOOD;
}

/* Reset the options for that vidcam. */
static void
stv680_init_options(Stv680_Vidcam * dev)
{
	int i;

	DBG(DBG_proc, "stv680_init_options: open\n");

	/* Pre-initialize the options. */
	memset(dev->opt, 0, sizeof(dev->opt));
	memset(dev->val, 0, sizeof(dev->val));

	for (i = 0; i < OPT_NUM_OPTIONS; ++i) {
		dev->opt[i].size = sizeof(SANE_Word);
		dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
	}
	DBG(DBG_proc,
	    "stv680_init_options: done loop opt_num_options=%d, i=%d \n",
	    OPT_NUM_OPTIONS, i);
	/* Number of options. */
	dev->opt[OPT_NUM_OPTS].name = "";
	dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
	dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
	dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
	dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
	dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;

	/* Mode group */
	dev->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode");
	dev->opt[OPT_MODE_GROUP].desc = "";	/* not valid for a group */
	dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
	dev->opt[OPT_MODE_GROUP].cap = 0;
	dev->opt[OPT_MODE_GROUP].size = 0;
	dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;

	/* Vidcam supported modes */
	dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
	dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
	dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
	dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
	dev->opt[OPT_MODE].size = max_string_size(scan_mode_list);
	dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
	dev->opt[OPT_MODE].constraint.string_list = scan_mode_list;
	dev->val[OPT_MODE].s = (SANE_Char *) strdup("");	/* will be set later */

	/* X and Y resolution */
	dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
	dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
	dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
	dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
	dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
	dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
	dev->val[OPT_RESOLUTION].w = dev->resolutions_list[dev->CIF + dev->QCIF + dev->VGA + dev->QVGA + dev->QSIF];	/* value will be 2 or 3 */

	/* brightness   */
	dev->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
	dev->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
	dev->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
	dev->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
	dev->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
	dev->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
	dev->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
	dev->val[OPT_BRIGHTNESS].w = 0;	/* to get middle value */

	/* Enhancement group */
	dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N("Enhancement");
	dev->opt[OPT_ENHANCEMENT_GROUP].desc = "";	/* not valid for a group */
	dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
	dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
	dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
	dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type =
		SANE_CONSTRAINT_NONE;

	/* red level calibration manual correction */
	dev->opt[OPT_WHITE_LEVEL_R].name = SANE_NAME_WHITE_LEVEL_R;
	dev->opt[OPT_WHITE_LEVEL_R].title = SANE_TITLE_WHITE_LEVEL_R;
	dev->opt[OPT_WHITE_LEVEL_R].desc = SANE_DESC_WHITE_LEVEL_R;
	dev->opt[OPT_WHITE_LEVEL_R].type = SANE_TYPE_INT;
	dev->opt[OPT_WHITE_LEVEL_R].unit = SANE_UNIT_NONE;
	dev->opt[OPT_WHITE_LEVEL_R].constraint_type = SANE_CONSTRAINT_RANGE;
	dev->opt[OPT_WHITE_LEVEL_R].constraint.range = &red_level_range;
	dev->val[OPT_WHITE_LEVEL_R].w = 00;	/* to get middle value */

	/* green level calibration manual correction */
	dev->opt[OPT_WHITE_LEVEL_G].name = SANE_NAME_WHITE_LEVEL_G;
	dev->opt[OPT_WHITE_LEVEL_G].title = SANE_TITLE_WHITE_LEVEL_G;
	dev->opt[OPT_WHITE_LEVEL_G].desc = SANE_DESC_WHITE_LEVEL_G;
	dev->opt[OPT_WHITE_LEVEL_G].type = SANE_TYPE_INT;
	dev->opt[OPT_WHITE_LEVEL_G].unit = SANE_UNIT_NONE;
	dev->opt[OPT_WHITE_LEVEL_G].constraint_type = SANE_CONSTRAINT_RANGE;
	dev->opt[OPT_WHITE_LEVEL_G].constraint.range = &green_level_range;
	dev->val[OPT_WHITE_LEVEL_G].w = 00;	/* to get middle value */

	/* blue level calibration manual correction */
	dev->opt[OPT_WHITE_LEVEL_B].name = SANE_NAME_WHITE_LEVEL_B;
	dev->opt[OPT_WHITE_LEVEL_B].title = SANE_TITLE_WHITE_LEVEL_B;
	dev->opt[OPT_WHITE_LEVEL_B].desc = SANE_DESC_WHITE_LEVEL_B;
	dev->opt[OPT_WHITE_LEVEL_B].type = SANE_TYPE_INT;
	dev->opt[OPT_WHITE_LEVEL_B].unit = SANE_UNIT_NONE;
	dev->opt[OPT_WHITE_LEVEL_B].constraint_type = SANE_CONSTRAINT_RANGE;
	dev->opt[OPT_WHITE_LEVEL_B].constraint.range = &blue_level_range;
	dev->val[OPT_WHITE_LEVEL_B].w = 00;	/* to get middle value */

	DBG(DBG_proc, "stv680_init_options: after blue level\n");

	/* Lastly, set the default scan mode. This might change some
	 * values previously set here. */

	sane_control_option(dev, OPT_MODE, SANE_ACTION_SET_VALUE,
			    (SANE_String_Const *) scan_mode_list[0], NULL);
	DBG(DBG_proc, "stv680_init_options: exit\n");
}

/* Read the image from the vidcam and fill the temporary buffer with it. */
static SANE_Status
stv680_fill_image(Stv680_Vidcam * dev)
{
	SANE_Status status;
	size_t size;
	size_t bulk_size_read;

	assert(dev->image_begin == dev->image_end);
	assert(dev->real_bytes_left > 0);

	DBG(DBG_proc, "stv680_fill_image: enter\n");

	DBG(DBG_proc, "stv680_fill_image: real dev bytes left=0x%lx \n",
	    (unsigned long) (size_t) dev->real_bytes_left);
	bulk_size_read = dev->real_bytes_left;

	while (dev->real_bytes_left) {
		/* Try to read the maximum number of bytes. */
		DBG(DBG_proc,
		    "stv680_fill_image: real dev bytes left, while loop=0x%lx \n",
		    (unsigned long) (size_t) dev->real_bytes_left);

		size = dev->real_bytes_left;
		if (size < bulk_size_read) {
			size = bulk_size_read;	/* it seems size can not be smaller then read by bulk */
		}
		if (size == 0) {
			/* Probably reached the end of the buffer. Check, just in case. */
			assert(dev->image_end != 0);
			return (SANE_STATUS_GOOD);
		}

		/* Do the transfer */

		DBG(DBG_proc,
		    "stv680_fill_image: dev->real_bytes_left: 0x%lx size: 0x%lx\n",
		    (unsigned long) (size_t) dev->real_bytes_left,
		    (unsigned long) (size_t) size);
		usleep(3000);
		/* urb 44 first read bulk */

		status = sanei_usb_read_bulk(dev->fd, dev->buffer, &size);

		if (status != SANE_STATUS_GOOD) {
			return status;
		}

		DBG(DBG_info,
		    "stv680_fill_image: size (read) = 0x%lx bytes (bpl=0x%lx)\n",
		    (unsigned long) (size_t) size,
		    (unsigned long) (size_t) dev->params.bytes_per_line);

		memcpy(dev->image + dev->image_end, dev->buffer, size);

		dev->image_end += size;
		bulk_size_read = size;
		if (dev->real_bytes_left > size)
			dev->real_bytes_left -= size;
		else if (dev->real_bytes_left <= size)	/* last loop */
			dev->real_bytes_left = 0;
		DBG(DBG_info, "stv680_fill_image: real bytes left = 0x%lx\n",
		    (unsigned long) (size_t) dev->real_bytes_left);
	}
	/*    i = stv_sndctrl (0, dev, 0x80, 0, &window, 0x02); *//* Get Last Error */
/*	DBG (DBG_proc, "STV(i): last error: %i,  command = 0x%x", window[0], window[1]);
	return -1; */
/*		
	}
	return 0;  */

	DBG(DBG_proc, "stv680_fill_image: exit\n");
	return (SANE_STATUS_GOOD);	/* unreachable */
}

#define MSG_MAXLEN   45
#define CHAR_HEIGHT  11
#define CHAR_WIDTH   6
#define CHAR_START   4

static SANE_Status
stv680_add_text(SANE_Byte * image, int width, int height, char *txt)
{
	SANE_Status status;
	time_t t;
	struct tm *tm;
	char line[MSG_MAXLEN + 1];
	SANE_Byte *ptr;
	int i, x, y, f, len;
	char fmtstring[25] = " %Y-%m-%d %H:%M:%S";
	char fmttxt[46];

	DBG(DBG_proc, "stv680_add_text: enter\n");
	time(&t);
	tm = localtime(&t);
	if (strlen(txt) > (MSG_MAXLEN - 23))
		strncpy(fmttxt, txt, (MSG_MAXLEN - 23));
	else
		strcpy(fmttxt, txt);
	strcat(fmttxt, fmtstring);

	len = strftime(line, MSG_MAXLEN, fmttxt, tm);

	for (y = 0; y < CHAR_HEIGHT; y++) {
		ptr = image + 3 * width * (height - CHAR_HEIGHT - 2 + y) + 12;

		for (x = 0; x < len; x++) {
			f = fontdata[line[x] * CHAR_HEIGHT + y];
			for (i = CHAR_WIDTH - 1; i >= 0; i--) {
				if (f & (CHAR_START << i)) {
					ptr[0] = 255;
					ptr[1] = 255;
					ptr[2] = 255;
				}
				ptr += 3;
			}	/* for i */
		}		/* for x */
	}			/* for y */

	DBG(DBG_proc, "stv680_add_text: exit vw=%d, vh=%d\n", width, height);
	status = (SANE_STATUS_GOOD);
	return status;

}

/* **************************  Video Decoding  *********************  */

static SANE_Status
stv680_bayer_unshuffle(Stv680_Vidcam * dev, SANE_Byte * buf, size_t * size)
{
	SANE_Status status;
	int x, y;
	int i = 0;
	int RED, GREEN, BLUE;
	int w = dev->cwidth;
	int vw = dev->x_resolution;
	int vh = dev->y_resolution;
	SANE_Byte p = 0;
	int colour = 0, bayer = 0;
	int bright_red;
	int bright_green;
	int bright_blue;
	int count;

	RED = dev->red_s;
	GREEN = dev->green_s;
	BLUE = dev->blue_s;

	DBG(DBG_proc, "stv680_bayer_unshuffle: enter\n");

#define AD(x, y, w) (((y)*(w)+(x))*3)

	DBG(DBG_proc,
	    "stv680_bayer_unshuffle: color read RED=%d, GREEN=%d, BLUE=%d\n",
	    RED, GREEN, BLUE);

	DBG(DBG_proc,
	    "stv680_bayer_unshuffle: w=%d, vw=%d, vh=%d, len=0x%lx\n", w, vw,
	    vh, (unsigned long) (size_t) size);

	for (y = 0; y < vh; y++) {
		for (x = 0; x < vw; x++) {
			if (x & 1) {
				p = dev->image[y * w + (x >> 1)];
			} else {
				p = dev->image[y * w + (x >> 1) + (w >> 1)];
			}
			if (y & 1)
				bayer = 2;
			else
				bayer = 0;
			if (x & 1)
				bayer++;

			switch (bayer) {
			case 0:
			case 3:
				colour = 1;
				break;
			case 1:
				colour = 0;
				break;
			case 2:
				colour = 2;
				break;
			}
			i = (y * vw + x) * 3;
			*(dev->output + i + colour) = (SANE_Byte) p;
		}		/* for x */

	}			/* for y */

	/****** gamma correction plus hardcoded white balance */
	/* Correction values red[], green[], blue[], are generated by 
	   (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255. 
	   White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and 
	   converted to unsigned char. Values are in stv680.h  */
	if (dev->scan_mode == STV680_COLOR_RGB
	    || dev->scan_mode == STV680_COLOR_RGB_TEXT) {
		for (y = 0; y < vh; y++) {
			for (x = 0; x < vw; x++) {
				i = (y * vw + x) * 3;
				*(dev->output + i) =
					red_g[*(dev->output + i)];
				*(dev->output + i + 1) =
					green_g[*(dev->output + i + 1)];
				*(dev->output + i + 2) =
					blue_g[*(dev->output + i + 2)];
			}
		}
	}
	DBG(DBG_proc, "stv680_bayer_unshuffle: gamma correction done\n");

	if (dev->scan_mode != STV680_COLOR_RAW) {

	/******  bayer demosaic  ******/
		for (y = 1; y < (vh - 1); y++) {
			for (x = 1; x < (vw - 1); x++) {	/* work out pixel type */
				if (y & 1)
					bayer = 0;
				else
					bayer = 2;
				if (!(x & 1))
					bayer++;
				switch (bayer) {
				case 0:	/* green. blue lr, red tb */
					*(dev->output + AD(x, y, vw) + BLUE) =
						((int)
						 *(dev->output +
						   AD(x - 1, y,
						      vw) + BLUE) +
						 (int) *(dev->output +
							 AD(x + 1, y,
							    vw) + BLUE)) >> 1;
					*(dev->output + AD(x, y, vw) + RED) =
						((int)
						 *(dev->output +
						   AD(x, y - 1,
						      vw) + RED) +
						 (int) *(dev->output +
							 AD(x, y + 1,
							    vw) + RED)) >> 1;
					break;

				case 1:	/* blue. green lrtb, red diagonals */
					*(dev->output + AD(x, y, vw) +
					  GREEN) =
		   ((int) *(dev->output + AD(x - 1, y, vw) + GREEN) +
		    (int) *(dev->output + AD(x + 1, y, vw) + GREEN) +
		    (int) *(dev->output + AD(x, y - 1, vw) + GREEN) +
		    (int) *(dev->output + AD(x, y + 1, vw) + GREEN)) >> 2;
					*(dev->output + AD(x, y, vw) + RED) =
						((int)
						 *(dev->output +
						   AD(x - 1, y - 1,
						      vw) + RED) +
						 (int) *(dev->output +
							 AD(x - 1, y + 1,
							    vw) + RED) +
						 (int) *(dev->output +
							 AD(x + 1, y - 1,
							    vw) + RED) +
						 (int) *(dev->output +
							 AD(x + 1, y + 1,
							    vw) + RED)) >> 2;
					break;

				case 2:	/* red. green lrtb, blue diagonals */
					*(dev->output + AD(x, y, vw) +
					  GREEN) =
		   ((int) *(dev->output + AD(x - 1, y, vw) + GREEN) +
		    (int) *(dev->output + AD(x + 1, y, vw) + GREEN) +
		    (int) *(dev->output + AD(x, y - 1, vw) + GREEN) +
		    (int) *(dev->output + AD(x, y + 1, vw) + GREEN)) >> 2;
					*(dev->output + AD(x, y, vw) + BLUE) =
						((int)
						 *(dev->output +
						   AD(x - 1, y - 1,
						      vw) + BLUE) +
						 (int) *(dev->output +
							 AD(x + 1, y - 1,
							    vw) + BLUE) +
						 (int) *(dev->output +
							 AD(x - 1, y + 1,
							    vw) + BLUE) +
						 (int) *(dev->output +
							 AD(x + 1, y + 1,
							    vw) + BLUE)) >> 2;
					break;

				case 3:	/* green. red lr, blue tb */
					*(dev->output + AD(x, y, vw) + RED) =
						((int)
						 *(dev->output +
						   AD(x - 1, y,
						      vw) + RED) +
						 (int) *(dev->output +
							 AD(x + 1, y,
							    vw) + RED)) >> 1;
					*(dev->output + AD(x, y, vw) + BLUE) =
						((int)
						 *(dev->output +
						   AD(x, y - 1,
						      vw) + BLUE) +
						 (int) *(dev->output +
							 AD(x, y + 1,
							    vw) + BLUE)) >> 1;
					break;
				}	/* switch */
			}	/* for x */
		}		/* for y  - end demosaic  */
	}			/* no bayer demosaic */
	DBG(DBG_proc, "stv680_bayer_unshuffle: bayer demosaic done\n");

	/* fix top and bottom row, left and right side */
	i = vw * 3;
	memcpy(dev->output, (dev->output + i), i);

	memcpy((dev->output + (vh * i)), (dev->output + ((vh - 1) * i)), i);


	for (y = 0; y < vh; y++) {
		i = y * vw * 3;
		memcpy((dev->output + i), (dev->output + i + 3), 3);
		memcpy((dev->output + i + (vw * 3)),
		       (dev->output + i + (vw - 1) * 3), 3);
	}

	/*  process all raw data, then trim to size if necessary */
	if (dev->subsample == 160) {
		i = 0;
		for (y = 0; y < vh; y++) {
			if (!(y & 1)) {
				for (x = 0; x < vw; x++) {
					p = (y * vw + x) * 3;
					if (!(x & 1)) {
						*(dev->output + i) =
							*(dev->output + p);
						*(dev->output + i + 1) =
							*(dev->output + p +
							  1);
						*(dev->output + i + 2) =
							*(dev->output + p +
							  2);
						i += 3;
					}
				}	/* for x */
			}
		}		/* for y */

		DBG(DBG_proc,
		    "stv680_bayer_unshuffle: if needed, trim to size 160 done\n");
	}
	/* reset to proper width */
	if ((dev->subsample == 160)) {
		vw = 160;
		vh = 120;
	}

	/* brightness adjustment */

	count = vw * vh * 3;

	bright_red =
		(dev->val[OPT_BRIGHTNESS].w) +
		(dev->val[OPT_WHITE_LEVEL_R].w);
	bright_green =
		(dev->val[OPT_BRIGHTNESS].w) +
		(dev->val[OPT_WHITE_LEVEL_G].w);
	bright_blue =
		(dev->val[OPT_BRIGHTNESS].w) +
		(dev->val[OPT_WHITE_LEVEL_B].w);

	for (x = 0; x < count; x++) {
		y = x + 1;
		i = x + 2;
		if ((*(dev->output + x) + bright_red) >= 255)
			*(buf + x) = 255;

		else if ((*(dev->output + x) + bright_red) <= 0)
			*(buf + x) = 0;
		else
			*(buf + x) = (*(dev->output + x) + bright_red);

		if ((*(dev->output + y) + bright_green) >= 255)
			*(buf + y) = 255;

		else if ((*(dev->output + y) + bright_green) <= 0)
			*(buf + y) = 0;
		else
			*(buf + y) = (*(dev->output + y) + bright_green);

		if ((*(dev->output + i) + bright_blue) >= 255)
			*(buf + i) = 255;

		else if ((*(dev->output + i) + bright_blue) <= 0)
			*(buf + i) = 0;
		else
			*(buf + i) = (*(dev->output + i) + bright_blue);

		x += 2;
	}

	if (dev->scan_mode == STV680_COLOR_RGB_TEXT) {
		strcpy(dev->picmsg_ps, "STVcam ");

		status = stv680_add_text(buf, vw, vh, dev->picmsg_ps);
		if (status != SANE_STATUS_GOOD) {
			DBG(DBG_info, "stv680_bayer_unshuffle status NOK\n");
			return (status);
		}
	}

	DBG(DBG_proc, "stv680_bayer_unshuffle: exit vw=%d, vh=%d\n", vw, vh);
	status = (SANE_STATUS_GOOD);
	return status;
}

/* Sane entry points */

SANE_Status
sane_init(SANE_Int * version_code, SANE_Auth_Callback authorize)
{
	FILE *fp;
	char line[PATH_MAX];
	size_t len;

	num_devices = 0;
	devlist = NULL;
	first_dev = NULL;

	DBG_INIT();

	DBG(DBG_sane_init, "sane_init\n");

	authorize = authorize;	/* silence gcc */

	DBG(DBG_error, "This is sane-stv680 version %d.%d-%d\n", V_MAJOR,
	    V_MINOR, BUILD);
	DBG(DBG_error, "(C) 2004-2006 by Gerard Klaver\n");

	if (version_code) {
		*version_code = SANE_CURRENT_VERSION;
	}

	DBG(DBG_proc, "sane_init: authorize %s null\n",
	    authorize ? "!=" : "==");

	sanei_usb_init();

	fp = sanei_config_open(STV680_CONFIG_FILE);
	if (!fp) {
		/* No default vidcam? */
		DBG(DBG_warning, "configuration file not found (%s)\n",
		    STV680_CONFIG_FILE);

		return SANE_STATUS_GOOD;
	}

	while (sanei_config_read(line, sizeof(line), fp)) {
		SANE_Word vendor;
		SANE_Word product;

		if (line[0] == '#')	/* ignore line comments */
			continue;
		len = strlen(line);

		if (!len)
			continue;	/* ignore empty lines */
		if (sscanf(line, "usb %i %i", &vendor, &product) == 2) {

			sanei_usb_attach_matching_devices(line, attach_one);
		} else {
			/* Garbage. Ignore. */
			DBG(DBG_warning,
			    "bad configuration line: \"%s\" - ignoring.\n",
			    line);
		}
	}

	fclose(fp);

	DBG(DBG_proc, "sane_init: leave\n");

	return SANE_STATUS_GOOD;
}

SANE_Status
sane_get_devices(const SANE_Device *** device_list, SANE_Bool local_only)
{
	Stv680_Vidcam *dev;
	int i;

	DBG(DBG_proc, "sane_get_devices: enter\n");

	local_only = local_only;	/* silence gcc */

	if (devlist)
		free(devlist);

	devlist = malloc((num_devices + 1) * sizeof(devlist[0]));
	if (!devlist)
		return SANE_STATUS_NO_MEM;

	i = 0;
	for (dev = first_dev; i < num_devices; dev = dev->next)
		devlist[i++] = &dev->sane;
	devlist[i++] = 0;

	*device_list = devlist;

	DBG(DBG_proc, "sane_get_devices: exit\n");

	return SANE_STATUS_GOOD;
}

SANE_Status
sane_open(SANE_String_Const devicename, SANE_Handle * handle)
{
	Stv680_Vidcam *dev;
	SANE_Status status;

	DBG(DBG_proc, "sane_open: enter\n");

	/* search for devicename */
	if (devicename[0]) {
		DBG(DBG_info, "sane_open: devicename=%s\n", devicename);

		for (dev = first_dev; dev; dev = dev->next) {
			if (strcmp(dev->sane.name, devicename) == 0) {
				break;
			}
		}

		if (!dev) {
			status = attach_vidcam(devicename, &dev);
			if (status != SANE_STATUS_GOOD) {
				return status;
			}
		}
	} else {
		DBG(DBG_sane_info,
		    "sane_open: no devicename, opening first device\n");
		dev = first_dev;	/* empty devicename -> use first device */
	}

	if (!dev) {
		DBG(DBG_error, "No vidcam found\n");

		return SANE_STATUS_INVAL;
	}

	stv680_init_options(dev);

	*handle = dev;

	DBG(DBG_proc, "sane_open: exit\n");

	return SANE_STATUS_GOOD;
}

const SANE_Option_Descriptor *
sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
{
	Stv680_Vidcam *dev = handle;

	DBG(DBG_proc, "sane_get_option_descriptor: enter, option %d\n",
	    option);

	if ((unsigned) option >= OPT_NUM_OPTIONS) {
		return NULL;
	}

	DBG(DBG_proc, "sane_get_option_descriptor: exit\n");

	return dev->opt + option;
}

SANE_Status
sane_control_option(SANE_Handle handle, SANE_Int option,
		    SANE_Action action, void *val, SANE_Int * info)
{
	Stv680_Vidcam *dev = handle;
	SANE_Status status;
	SANE_Word cap;

	DBG(DBG_proc, "sane_control_option: enter, option %d, action %d\n",
	    option, action);

	if (info) {
		*info = 0;
	}

	if (dev->scanning) {
		return SANE_STATUS_DEVICE_BUSY;
	}

	if (option < 0 || option >= OPT_NUM_OPTIONS) {
		return SANE_STATUS_INVAL;
	}

	cap = dev->opt[option].cap;
	if (!SANE_OPTION_IS_ACTIVE(cap)) {
		return SANE_STATUS_INVAL;
	}

	if (action == SANE_ACTION_GET_VALUE) {

		switch (option) {
			/* word options */
		case OPT_NUM_OPTS:
		case OPT_RESOLUTION:
		case OPT_BRIGHTNESS:
		case OPT_WHITE_LEVEL_R:
		case OPT_WHITE_LEVEL_G:
		case OPT_WHITE_LEVEL_B:
			*(SANE_Word *) val = dev->val[option].w;
			return SANE_STATUS_GOOD;
		case OPT_MODE:
			strcpy(val, dev->val[option].s);
			return SANE_STATUS_GOOD;
		default:
			return SANE_STATUS_INVAL;
		}
	} else if (action == SANE_ACTION_SET_VALUE) {

		if (!SANE_OPTION_IS_SETTABLE(cap)) {
			DBG(DBG_error,
			    "could not set option, not settable\n");
			return SANE_STATUS_INVAL;
		}

		status = sanei_constrain_value(dev->opt + option, val, info);
		if (status != SANE_STATUS_GOOD) {
			DBG(DBG_error,
			    "could not set option, invalid value\n");
			return status;
		}

		switch (option) {

			/* Numeric side-effect options */
		case OPT_RESOLUTION:
		case OPT_BRIGHTNESS:
		case OPT_WHITE_LEVEL_R:
		case OPT_WHITE_LEVEL_G:
		case OPT_WHITE_LEVEL_B:
			if (info) {
				*info |= SANE_INFO_RELOAD_PARAMS;
			}
			dev->val[option].w = *(SANE_Word *) val;
			return SANE_STATUS_GOOD;

			/* String side-effect options */
		case OPT_MODE:
			if (strcmp(dev->val[option].s, val) == 0)
				return SANE_STATUS_GOOD;

			free(dev->val[OPT_MODE].s);
			dev->val[OPT_MODE].s = (SANE_Char *) strdup(val);

			dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE;
			dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE;
			dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE;

			if (strcmp(dev->val[OPT_MODE].s, COLOR_RAW_STR) == 0) {
				dev->scan_mode = STV680_COLOR_RAW;
			} else if (strcmp(dev->val[OPT_MODE].s, COLOR_RGB_STR)
				   == 0) {
				dev->scan_mode = STV680_COLOR_RGB;
			} else if (strcmp
				   (dev->val[OPT_MODE].s,
				    SANE_VALUE_SCAN_MODE_COLOR)
				   == 0) {
				dev->scan_mode = STV680_COLOR;

			} else if (strcmp
				   (dev->val[OPT_MODE].s,
				    COLOR_RGB_TEXT_STR) == 0) {
				dev->scan_mode = STV680_COLOR_RGB_TEXT;

			}

			/* The STV680 supports only a handful of resolution. */
			/* This the default resolution range for the STV680 */

			dev->depth = 8;
			if (dev->resolutions_list != NULL) {
				int i;

				dev->opt[OPT_RESOLUTION].constraint_type =
					SANE_CONSTRAINT_WORD_LIST;
				dev->opt[OPT_RESOLUTION].constraint.
					word_list = dev->resolutions_list;

				/* If the resolution isn't in the list, set a default. */
				for (i = 1; i <= dev->resolutions_list[0];
				     i++) {
					if (dev->resolutions_list[i] >=
					    dev->val[OPT_RESOLUTION].w)
						break;
				}
				if (i > dev->resolutions_list[0]) {
					/* Too big. Take lowest. */
					dev->val[OPT_RESOLUTION].w =
						dev->resolutions_list[1];
				} else {
					/* Take immediate superioir value. */
					dev->val[OPT_RESOLUTION].w =
						dev->resolutions_list[i];
				}
			}

			/* String side-effect options */

			if (info) {
				*info |= SANE_INFO_RELOAD_OPTIONS |
					SANE_INFO_RELOAD_PARAMS;
			}
			return SANE_STATUS_GOOD;
		default:
			return SANE_STATUS_INVAL;
		}
	}

	DBG(DBG_proc, "sane_control_option: exit, bad\n");

	return SANE_STATUS_UNSUPPORTED;
}

SANE_Status
sane_get_parameters(SANE_Handle handle, SANE_Parameters * params)
{
	Stv680_Vidcam *dev = handle;
	int i;

	DBG(DBG_proc, "sane_get_parameters: enter\n");

	if (!(dev->scanning)) {
		dev->x_resolution = dev->val[OPT_RESOLUTION].w;
		/* Prepare the parameters for the caller. */
		memset(&dev->params, 0, sizeof(SANE_Parameters));

		dev->params.last_frame = SANE_TRUE;

		switch (dev->scan_mode) {
		case STV680_COLOR_RAW:
			dev->bytes_pixel = 1;	/* raw image is 422 code, 1 byte/pixel */
			break;
		case STV680_COLOR_RGB:
		case STV680_COLOR_RGB_TEXT:
		case STV680_COLOR:
			dev->bytes_pixel = 3;
			break;
		}
		dev->params.format = SANE_FRAME_RGB;
		dev->params.pixels_per_line = dev->x_resolution;
		dev->params.bytes_per_line =
			dev->params.pixels_per_line * dev->bytes_pixel;
		dev->params.depth = 8;
		if (dev->resolutions_list != NULL) {
			/* This vidcam has a fixed number of supported
			 * resolutions. Find the color sequence for that
			 * resolution. */

			for (i = 0;
			     dev->hw->color_adjust[i].resolution_x !=
			     dev->x_resolution; i++);

			dev->red_s = dev->hw->color_adjust[i].z1_color_0;
			dev->green_s = dev->hw->color_adjust[i].z1_color_1;
			dev->blue_s = dev->hw->color_adjust[i].z1_color_2;
			dev->y_resolution =
				dev->hw->color_adjust[i].resolution_y;
		}
		dev->subsample = 0;
		switch (dev->val[OPT_RESOLUTION].w) {
		case 176:
			dev->video_mode = 0x0200;
			dev->cwidth = dev->x_resolution + 2;
			dev->cheight = dev->y_resolution + 2;
			break;
		case 160:	/* 160x120 subsampled */
			dev->x_resolution = 320;
			dev->y_resolution = 240;
			dev->video_mode = 0x0300;
			dev->cwidth = dev->x_resolution + 2;
			dev->cheight = dev->y_resolution + 2;
			dev->subsample = 160;
			break;
		case 320:
			dev->video_mode = 0x0300;
			dev->cwidth = dev->x_resolution + 2;
			dev->cheight = dev->y_resolution + 2;
			break;
		case 352:
			dev->video_mode = 0x0000;
			dev->cwidth = dev->x_resolution + 4;
			dev->cheight = dev->y_resolution + 4;
			break;
		case 640:
			dev->video_mode = 0x0100;
			dev->cwidth = dev->x_resolution + 4;
			dev->cheight = dev->y_resolution + 4;
			break;
		}
		dev->params.pixels_per_line = dev->x_resolution;
		dev->params.lines = dev->y_resolution;
		DBG(DBG_info, "sane_get_parameters: x=%d, y=%d\n",
		    dev->x_resolution, dev->y_resolution);
	}

	/* Return the current values. */
	if (params) {
		*params = (dev->params);
	}

	DBG(DBG_proc, "sane_get_parameters: exit\n");

	return SANE_STATUS_GOOD;
}

SANE_Status
sane_start(SANE_Handle handle)
{
	Stv680_Vidcam *dev = handle;
	SANE_Status status;

	DBG(DBG_proc, "sane_start: enter\n");

	if (!(dev->scanning)) {
		sane_get_parameters(dev, NULL);

		/* Open again the vidcam  */
		if (sanei_usb_open(dev->devicename, &(dev->fd)) != 0) {
			DBG(DBG_error, "ERROR: sane_start: open failed\n");
			return SANE_STATUS_INVAL;
		}

		/* Initialize the vidcam. */
		status = stv680_vidcam_init(dev);
		if (status) {
			DBG(DBG_error, "ERROR: failed to init the vidcam\n");
			stv680_close(dev);
			return status;
		}

	}

	dev->image_end = 0;
	dev->image_begin = 0;
	/* real_byte_left is bulk read bytes, bytes_left is frontend buffer bytes */
	dev->real_bytes_left = dev->cwidth * dev->cheight;
	dev->bytes_left = dev->params.bytes_per_line * dev->params.lines;

	dev->scanning = SANE_TRUE;

	DBG(DBG_proc, "sane_start: exit\n");

	return SANE_STATUS_GOOD;
}

SANE_Status
sane_read(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
	  SANE_Int * len)
{
	SANE_Status status;
	Stv680_Vidcam *dev = handle;
	size_t size;

	DBG(DBG_proc, "sane_read: enter\n");

	*len = 0;
	if (dev->deliver_eof) {
		dev->deliver_eof = 0;
		return SANE_STATUS_EOF;
	}

	if (!(dev->scanning)) {
		/* OOPS, not scanning, stop a scan. */
		stv680_reset_vidcam(dev);
		stv680_close(dev);
		dev->scanning = SANE_FALSE;
		return SANE_STATUS_CANCELLED;
	}

	if (dev->bytes_left <= 0) {
		return (SANE_STATUS_EOF);
	}

	if (dev->image_begin == dev->image_end) {
		/* Fill image */
		status = stv680_fill_image(dev);
		if (status != SANE_STATUS_GOOD) {
			DBG(DBG_info,
			    "sane_read: stv680_fill_image status NOK\n");
			return (status);
		}
	}

	/* Something must have been read */
	if (dev->image_begin == dev->image_end) {
		DBG(DBG_info, "sane_read: nothing read\n");
		return SANE_STATUS_IO_ERROR;
	}

	size = dev->bytes_left;
	if (((unsigned int) max_len) < size) {
		DBG(DBG_error, "sane_read: max_len < size\n");
		return (SANE_FALSE);
	}
	if ((dev->image_end - dev->image_begin) > size) {
		size = dev->image_end - dev->image_begin;
		DBG(DBG_proc,
		    "sane_read: size < dev->image_end - dev->image_begin\n");
	}
	/* diff between size an dev->bytes_left because of 356/352 and 292/288 */
	DBG(DBG_info, "sane_read: size =0x%lx bytes, max_len=0x%lx bytes\n",
	    (unsigned long) (size_t) size, (unsigned long) (size_t) max_len);

	*len = dev->bytes_left;	/* needed */
	size = dev->bytes_left;
	dev->bytes_left = 0;	/* needed for frontend or ? */

	if (dev->scan_mode != STV680_COLOR_RAW) {
		/* do bayer unshuffle  after complete frame is read */
		status = stv680_bayer_unshuffle(dev, buf, &size);
		if (status != SANE_STATUS_GOOD) {
			DBG(DBG_info,
			    "sane_read: stv680_bayer_unshuffle status NOK\n");
			return (status);
		}
	} else {
		/* Copy the raw data to the frontend buffer. */
		memcpy(buf, dev->image, size);
		DBG(DBG_info, "sane_read: raw mode\n");
	}
	DBG(DBG_info, "sane_read: exit\n");
	status = SANE_STATUS_GOOD;
	return status;
}

SANE_Status
sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking)
{

	DBG(DBG_proc, "sane_set_io_mode: enter\n");

	handle = handle;	/* silence gcc */
	non_blocking = non_blocking;	/* silence gcc */


	DBG(DBG_proc, "sane_set_io_mode: exit\n");

	return SANE_STATUS_UNSUPPORTED;
}

SANE_Status
sane_get_select_fd(SANE_Handle handle, SANE_Int * fd)
{
	DBG(DBG_proc, "sane_get_select_fd: enter\n");

	handle = handle;	/* silence gcc */
	fd = fd;		/* silence gcc */

	DBG(DBG_proc, "sane_get_select_fd: exit\n");

	return SANE_STATUS_UNSUPPORTED;
}

void
sane_cancel(SANE_Handle handle)
{
	Stv680_Vidcam *dev = handle;

	DBG(DBG_proc, "sane_cancel: enter\n");

	/* Stop a scan. */
	if (dev->scanning == SANE_TRUE) {
		/* Reset the vidcam */
		stv680_reset_vidcam(dev);
		stv680_close(dev);
	}
	dev->scanning = SANE_FALSE;
	dev->deliver_eof = 0;

	/* return SANE_STATUS_CANCELLED; */
	DBG(DBG_proc, "sane_cancel: exit\n");
}

void
sane_close(SANE_Handle handle)
{
	Stv680_Vidcam *dev = handle;
	Stv680_Vidcam *dev_tmp;

	DBG(DBG_proc, "sane_close: enter\n");

/* Stop a scan. */

	if (dev->scanning == SANE_TRUE) {
		stv680_reset_vidcam(dev);
		stv680_close(dev);
	}
	dev->scanning = SANE_FALSE;

	/* Unlink dev. */
	if (first_dev == dev) {
		first_dev = dev->next;
	} else {
		dev_tmp = first_dev;
		while (dev_tmp->next && dev_tmp->next != dev) {
			dev_tmp = dev_tmp->next;
		}
		if (dev_tmp->next != NULL) {
			dev_tmp->next = dev_tmp->next->next;
		}
	}

	stv680_free(dev);
	num_devices--;

	DBG(DBG_proc, "sane_close: exit\n");
}

void
sane_exit(void)
{
	DBG(DBG_proc, "sane_exit: enter\n");

	while (first_dev) {
		sane_close(first_dev);
	}

	if (devlist) {
		free(devlist);
		devlist = NULL;
	}

	DBG(DBG_proc, "sane_exit: exit\n");
}
